package com.five.sso.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.five.core.exception.CustomerException;
import com.five.core.result.ResultEnum;
import com.five.core.utils.JsonUtils;
import com.five.core.utils.Md5Utils;
import com.five.pojo.entity.User;
import com.five.sso.dto.MsgLoginDto;
import com.five.sso.dto.RegisterDto;
import com.five.sso.repository.UserRepository;
import com.five.sso.service.UserService;
import com.five.sso.utils.CheckMobile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    //用户token，K值
    public final static String TOKEN_KEY = "USER_TOKEN_KEY";
    //用户token过期时间
    public final static Integer TOKEN_EXPIRE = 60 * 60 * 2;
    //mobile 用户
    public static final String MOBILE_LOGGED_IN_KEY = "USER_MOBILE:LOGGED_IN";
    //pc 用户
    public static final String PC_LOGGED_IN_KEY = "USER_PC:LOGGED_IN";
    //mobile 最大用户
    public static final Long MAX_LOGIN_MOBILE_DEVICE = 3L;
    //pc 最大用户
    public static final Long MAX_LOGIN_PC_DEVICE = 3L;

    @Resource
    private UserRepository userRepository;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    //获取ip地址使用
    @Resource
    private HttpServletRequest request;

    @Override
    public int register(RegisterDto registerDto) {
        if (!registerDto.getPassword().equals(registerDto.getPassword2())) {
            throw new CustomerException(ResultEnum.TWICE_PASSWORD_IS_NOT_TRUE);
        }

        User user = new User();
        BeanUtils.copyProperties(registerDto, user);

        if (userRepository.findUserByUsername(user.getUsername()) != null) {
            throw new CustomerException(ResultEnum.USER_NAME_HAVE);
        }

        if (userRepository.findUserByPhone(user.getPhone()) != null) {
            throw new CustomerException(ResultEnum.PHONE_EXIT);
        }

        if (userRepository.findUserByEmail(user.getEmail()) != null) {
            throw new CustomerException(ResultEnum.USER_EMAIL_HAVA);
        }

        String serverCode = (String) redisTemplate.opsForValue().get(SendMsgServiceImpl.REP_MSG + user.getPhone());

        if (StrUtil.isEmpty(serverCode)) {
            throw new CustomerException(ResultEnum.CODE_IS_EMPTY);
        }

        if (!serverCode.equals(registerDto.getCode())) {
            throw new CustomerException(ResultEnum.CODE_ERROR);
        }
        String salt = Md5Utils.createSalt();
        String newpwd = Md5Utils.md5Password(user.getPassword(), salt);

        user.setSalt(salt);
        user.setPassword(newpwd);
        user.setCreated(new Date());
        user.setImage("https://hyimagedgty.oss-cn-beijing.aliyuncs.com/LIUTAO/13db1546-6ce4-457a-9415-af654f165158.jpg");
        userRepository.save(user);
        log.info("用户注册成功");
        //删除 redis 中注册信息
        redisTemplate.delete(SendMsgServiceImpl.REP_MSG + user.getPhone());
        return 1;
    }

    @Override
    public String loginByPhone(MsgLoginDto msgLoginDto, String userAgent) {
        User userByPhone = userRepository.findUserByPhone(msgLoginDto.getPhone());

        //手机号不存在
        if (userByPhone==null){
            throw new CustomerException(ResultEnum.NOT_USER_PHONE);
        }

        String serverCode = (String) redisTemplate.opsForValue().get(SendMsgServiceImpl.LONG_MSG + userByPhone.getPhone());

        if (StrUtil.isEmpty(serverCode)){
            throw new CustomerException(ResultEnum.CODE_IS_EMPTY);
        }

        if (!serverCode.equals(msgLoginDto.getCode())){
            throw new CustomerException(ResultEnum.CODE_ERROR);
        }

        return CheckLogin(userByPhone,userAgent);
    }

    @Override
    public String oneLoginByPhone(MsgLoginDto msgLoginDto, String userAgent) {
        System.err.println(msgLoginDto);
        String serverCode =(String) redisTemplate.opsForValue().get(SendMsgServiceImpl.ONE_LOGIN_MSG + msgLoginDto.getPhone());
        if (StrUtil.isEmpty(serverCode)) {
            throw new CustomerException(ResultEnum.CODE_IS_EMPTY);
        }

        if (!serverCode.equals(msgLoginDto.getCode())) {
            throw new CustomerException(ResultEnum.CODE_ERROR);
        }

        //判断用户当前手机号状态
        User userByPhone = userRepository.findUserByPhone(msgLoginDto.getPhone());
        if (userByPhone==null){
            userByPhone = User.builder()
                    .phone(msgLoginDto.getPhone())
                    .username("LIUTAO"+msgLoginDto.getPhone().substring(7))
                    .created(new Date())
                    .image("https://hyimagedgty.oss-cn-beijing.aliyuncs.com/LIUTAO/13db1546-6ce4-457a-9415-af654f165158.jpg")
                    .build();
            userRepository.save(userByPhone);
        }

        return CheckLogin(userByPhone,userAgent);
    }

    @Override
    public int loginOut(String token, String userAgent) {
        //获得当前token
        String userToken =(String) redisTemplate.opsForValue().get(TOKEN_KEY + token);
        if (userToken==null){
            return  1;
        }
        log.info("用户退出登录");
        boolean isMobile = CheckMobile.check(userAgent);
        String devicePrefix = isMobile ? MOBILE_LOGGED_IN_KEY : PC_LOGGED_IN_KEY;

        redisTemplate.opsForList().remove(devicePrefix+token,1,token);
        redisTemplate.delete(TOKEN_KEY+token);

        return 1;
    }

    private String CheckLogin(User user, String userAgent) {
        //检查属于那个端
        boolean isMobile = CheckMobile.check(userAgent);

        String devicePrefix = isMobile? MOBILE_LOGGED_IN_KEY:PC_LOGGED_IN_KEY ;
        Long loginSize = isMobile ? MAX_LOGIN_MOBILE_DEVICE : MAX_LOGIN_PC_DEVICE;

        String userListKey =devicePrefix+user.getId();

        //先判断token list 的 过期内容，进行刷新后在做后续操作
        cleanTokensList(userListKey);

        Long size = redisTemplate.opsForList().size(userListKey);
        if (size!=null && size>=loginSize){
            String deviceType = isMobile ? "移动端" : "PC端";
            log.warn("用户[{}]在{}登录次数已达上限（{}次），登录失败", user.getId(), deviceType, loginSize);
            throw new CustomerException(ResultEnum.LOGIN_COUNT_EXCEED);
        }

        //生成token
        String token = UUID.randomUUID().toString(true);

        //redis 存储token
        redisTemplate.opsForValue().set(TOKEN_KEY+token,user.getId().toString(),TOKEN_EXPIRE, TimeUnit.SECONDS);

        //存储 redis list 列表
        redisTemplate.opsForList().rightPush(userListKey,token);
        redisTemplate.expire(userListKey,TOKEN_EXPIRE,TimeUnit.SECONDS);

        // 更新用户登录信息
        user.setLastLoginTime(new Date());
        user.setIp(request.getRemoteAddr());

        // 删除验证码
        redisTemplate.delete(SendMsgServiceImpl.LONG_MSG + user.getPhone());
        redisTemplate.delete(SendMsgServiceImpl.ONE_LOGIN_MSG + user.getPhone());

        userRepository.save(user);

        Long finalCount = redisTemplate.opsForList().size(userListKey);
        log.info("用户[{}]在{}登录成功，当前登录次数：{}次", user.getId(), isMobile ? "移动端" : "PC端", finalCount);

        return token;
    }

    private void cleanTokensList(String userListKey) {
        List<Object> tokens = redisTemplate.opsForList().range(userListKey, 0, -1);
        if (tokens == null || tokens.isEmpty()) {
            return; // 空列表无需清理
        }
        tokens.forEach(token->{
            String tokenKey = TOKEN_KEY+token;
            Boolean tokenExists = redisTemplate.hasKey(tokenKey);
            System.out.println("tokenExists = " + tokenExists);
            if (Boolean.FALSE.equals(tokenExists)){
                redisTemplate.opsForList().remove(userListKey,1,token);
                log.info("清理token{}.所在列表{}",token,userListKey);
            }
        });
    }

}
